Failed Conditions
Push — master ( c3e11c...1d8d9e )
by Yo
02:09 queued 37s
created

Router.all(ꞌ/sarah/:nameꞌ)   D

Complexity

Conditions 10
Paths 25

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 25
nop 1
dl 0
loc 37
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like Router.all(ꞌ/sarah/:nameꞌ) often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
  var request    = require('request');
2
  var express    = require('express');
3
  var FeedParser = require('feedparser');
4
  var extend     = require('extend');
5
  var ent        = require('ent');
6
  
7
  var USERAGENT  = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.75 Safari/537.1";
8
  
9
// ------------------------------------------
10
//  CONSTRUCTOR
11
// ------------------------------------------
12
13
var init = function(){
14
  info('Starting SARAH ...');
15
  global.SARAH = SARAH;
16
  
17
  SARAH.ConfigManager  = require('./config.js').init();
18
  SARAH.PluginManager  = require('./plugin.js').init();
19
  SARAH.LangManager    = require('./lang.js').init();
20
  SARAH.PrivacyManager = require('./privacy.js').init();
21
  SARAH.PortalManager  = require('./portal.js').init();
22
  SARAH.ScriptManager  = require('./script.js').init();
23
  SARAH.RuleEngine     = require('./rules.js').init();
24
  SARAH.CRONManager    = require('./cron.js').init();
25
  SARAH.ProfileManager = require('./profile.js').init();
26
  SARAH.Marketplace    = require('./marketplace.js').init();
27
  
28
  /*
29
  SARAH.PhantomManager = require('./phantom.js').init();
30
  */
31
  
32
  SARAH.run      = SARAH.ScriptManager.run;
33
  SARAH.call     = SARAH.ScriptManager.call;
34
  SARAH.last     = SARAH.ScriptManager.last;
35
  SARAH.find     = SARAH.PluginManager.find;
36
  SARAH.exists   = SARAH.PluginManager.exists;
37
  SARAH.trigger  = SARAH.PluginManager.trigger;
38
  SARAH.listen   = SARAH.PluginManager.listen;
39
  
40
  return SARAH;
41
}
42
43
// ------------------------------------------
44
//  RSS
45
// ------------------------------------------
46
47
var RSSFeedCache = {};
48
var getRSSFeed = function(url, cache){
49
  
50
  // Use cache
51
  if (!cache && RSSFeedCache[url]){ return RSSFeedCache[url]; }
52
  
53
  var feed = { items : [] };
54
  request(url)
55
  .pipe(new FeedParser())
56
  .on('meta', function (meta) { feed.meta = meta; })
57
  .on('readable', function() {
58
    var stream = this, item;
59
    while (item = stream.read()) { 
60
      item.description = ent.decode(item.description);
61
      feed.items.push(item);
62
    }
63
    RSSFeedCache[url] = feed; // Cache
64
  });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
65
}
66
67
// ------------------------------------------
68
//  ASKME
69
// ------------------------------------------
70
71
var ASKME = false;
72
var stack = [];
73
74
var end = function(){ 
75
  ASKME = false;  next(); 
76
}
77
78
var next = function(){
79
  if (stack.length <= 0){ 
80
    return SARAH.context('default');
81
  }
82
  var args = stack.shift();
83
  askme(args[0], args[1], args[2], args[3])
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
84
}
85
86
var askme = function(tts, grammar, timeout, callback, wrong){
87
  if (!grammar) { return; }
88
  if (!callback){ return; }
89
  if (ASKME)  { return stack.push(arguments); }
90
  
91
  // Build request
92
  info('AskMe', ASKME);
93
  ASKME = { 'sentences':[], 'tags':[] }
94
  if (tts){ 
95
    ASKME.tts = tts;
96
    ASKME.sync = true;
97
    ASKME.wrong = wrong;
98
  }
99
  for (var g in grammar){
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
100
    ASKME.sentences.push(g);
101
    ASKME.tags.push(grammar[g]);
102
  }
103
  
104
  // Send request
105
  remote(ASKME);
106
  
107
  // Backup
108
  ASKME.rule     = grammar
109
  ASKME.callback = callback;
110
  ASKME.token    = setTimeout(function(){
111
    ASKME = false;
112
    if (timeout <= 0){
113
      callback(false, end);
114
    } else {
115
      SARAH.askme(tts, grammar, 0, callback);
116
    }
117
  }, timeout || 16000);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
118
}
119
120
var answerme = function(req, res, next){ res.end();
0 ignored issues
show
Unused Code introduced by
The parameter next is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
121
  if (!ASKME){ return; }
122
  if (ASKME.token){ clearTimeout(ASKME.token); }
123
  ASKME.callback(req.param('dictation') || req.param('tag'), end);
124
}
125
126
// ------------------------------------------
127
//  ROUTER
128
// ------------------------------------------
129
130
var Router = express.Router();
131
132
Router.all('/sarah/:name', function(req, res, next) {
0 ignored issues
show
Unused Code introduced by
The parameter next is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
133
134
  var name = req.params.name;
135
  var options = {};
136
  extend(true, options, req.query);
137
  extend(true, options, req.body);
138
  
139
  // 1. Log action into rule engine
140
  var entry = SARAH.RuleEngine.log(name, options);
141
  // info('Rule Engine Log:', entry);
142
  
143
  // 3. Send back TTS
144
  var callback = function(data){
145
    
146
    // Redirect to portlet (no rules, asknect, ...)
147
    if (req.query.ajax){
148
      res.redirect('/plugin/'+name);
149
      return;
150
    }
151
    
152
    //res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
153
    if (!res.headersSent){
154
      res.set({'Content-Type': 'text/html; charset=utf-8'});
155
    }
156
    
157
    // Speak
158
    if (data && data.tts){
159
      var tts = SARAH.ScriptManager.speak(data.tts);
160
      if (tts){ res.write(tts); }
161
    }
162
    
163
    res.status(200).end();
164
    
165
    // Ask next from data
166
    if (data && data.asknext){
167
      if (typeof data.asknext === 'string'){
168
        SARAH.asknext(data.asknext);
169
      } else {
170
        SARAH.asknext(data.asknext.rule, data.asknext);
171
      }
172
    }
173
    else if (options.asknext){
174
      SARAH.asknext(options.asknext);
175
    }
176
    
177
    // 4. Guess next action from RuleEngine
178
    var next = SARAH.RuleEngine.next(entry);
0 ignored issues
show
Unused Code introduced by
The assignment to variable next seems to be never used. Consider removing it.
Loading history...
179
    //info('Rule Engine Guess:', next);
180
  }
181
  
182
  // 2. Run plugin's script
183
  SARAH.run(name, options, callback, true);
184
});
185
186
Router.all('/standby', function(req, res, next) { 
0 ignored issues
show
Unused Code introduced by
The parameter next is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
187
  var motion = req.query.motion == "True" ? true : false;
188
  SARAH.ScriptManager.standBy(motion, req.query.client);
189
  res.end(); 
190
});
191
192
Router.all('/askme', answerme);
193
194
195
// ------------------------------------------
196
//  REMOTE
197
// ------------------------------------------
198
199
var _callback = function (err, response, body){
200
  if (err || response.statusCode != 200) {
201
    warn("HTTP Error: ", err, response, body);
202
    return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
203
  }
204
};
205
206
var remote = function(query, callback){
207
  var url = Config.http.remote;
0 ignored issues
show
Bug introduced by
The variable Config seems to be never declared. If this is a global, consider adding a /** global: Config */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
208
  var querystring = require('querystring');
209
  url += '?' + querystring.stringify(query);
210
211
  info('Remote: ', url);
212
  var request = require('request');
213
  request({ 'url' : url }, callback || _callback);
214
};
215
216
var speak = function(tts, callback){
217
  
218
  tts = SARAH.ScriptManager.speak(tts, callback);
219
  if (!tts){ return; }
220
  
221
  // Hook for TTS
222
  var qs = { 
223
    'tts'  : tts,
224
    'sync' : callback ? true : false
225
  };
226
  return remote(qs, callback);
227
}
228
229
var answer = function(tts, callback){
230
  var answers = Config.bot.answers.split('|');
0 ignored issues
show
Bug introduced by
The variable Config seems to be never declared. If this is a global, consider adding a /** global: Config */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
231
  var answer = answers[ Math.floor(Math.random() * answers.length)];
232
  return speak(answer, callback);
233
}
234
235
var shutup = function(once){
236
  var qs = { 'notts' : 'true' }
237
  if (once){ qs.once = true; }
238
  return remote(qs);
239
}
240
241
var play = function(path, callback){
242
  // Hook for TTS
243
  var qs = { 
244
    'play'  : path,
245
    'sync' : callback ? true : false
246
  };
247
  return remote(qs, callback);
248
}
249
250
var stop = function(path){
251
  return remote({ 'stop' : path });
252
}
253
254
var runApp = function(path, params){
255
  var qs = { 'run' : path };
256
  if (params){ qs.runp = params; }
257
  return remote(qs);
258
}
259
260
var activate = function(process){
261
  return remote({ 'activate' : process });
262
}
263
264
var keyText = function(text){
265
  return remote({ 'keyText' : text });
266
}
267
268
var keyUp = function(key, mod){
269
  var qs = { 'keyUp' : key };
270
  if (mod){ qs.keyMod = mod; }
271
  return remote(qs);
272
}
273
274
var keyDown = function(key, mod){
275
  var qs = { 'keyDown' : key };
276
  if (mod){ qs.keyMod = mod; }
277
  return remote(qs);
278
}
279
280
var keyPress = function(key, mod){
281
  var qs = { 'keyPress' : key };
282
  if (mod){ qs.keyMod = mod; }
283
  return remote(qs);
284
}
285
286
var face = function(pause){
287
  return remote({ 'face' : pause });
288
}
289
290
var gesture = function(pause){
291
  return remote({ 'gesture' : pause });
292
}
293
294
var listen = function(pause){
295
  return remote({ 'listen' : pause });
296
}
297
298
var picture = function(device, path, type){
299
  var qs = { 'picture' : type || 'true'  };
300
301
  if (path){ qs.picture = path; }
302
  if (device){ qs.device = device; }
303
  return remote(qs);
304
}
305
306
var recognize = function(path){
307
  return remote({ 'recognize' : path });
308
}
309
310
var context = function(rules){
311
  return remote({ 'context' : rules });
312
}
313
314
var grammar = function(rule, xml){
315
  return remote({ 'grammar' : rule , 'xml' : xml});
316
}
317
318
var asknext = function(rule, options){
319
  var qs = {'asknext' : rule };
320
  if (options){  extend(true, qs, options); }
321
  return remote(qs);
322
}
323
324
// ------------------------------------------
325
//  PUBLIC
326
// ------------------------------------------
327
328
var SARAH = {
329
  'init'      : init,
330
  'remote'    : remote,
331
              
332
  'speak'     : speak,
333
  'answer'    : answer,
334
  'shutup'    : shutup,
335
  'play'      : play,
336
  'stop'      : stop,
337
              
338
  'runApp'    : runApp,
339
  'activate'  : activate,
340
  'keyText'   : keyText,
341
  'keyUp'     : keyUp,
342
  'keyDown'   : keyDown,
343
  'keyPress'  : keyPress,
344
              
345
  'face'      : face,
346
  'gesture'   : gesture,
347
  'listen'    : listen,
348
  'picture'   : picture,
349
  'recognize' : recognize,
350
  
351
  'context'   : context,
352
  'grammar'   : grammar,
353
  'askme'     : askme,
354
  'asknext'   : asknext,
355
  
356
  'getRSSFeed' : getRSSFeed,
357
  'Router'     : Router,
358
  'USERAGENT'  : USERAGENT
359
}
360
361
// Exports SARAH singleton
362
exports.init = init;